import { GLTFLoader } from '../libs/three137/GLTFLoader.js';
import { RGBELoader } from '../libs/three137/RGBELoader.js';
import { LoadingBar } from '../libs/LoadingBar.js';
import { urls } from '../assets/store.js';
import {
	AudioListener, DirectionalLight, Color, Clock, PerspectiveCamera, Fog, HemisphereLight,
	sRGBEncoding, PMREMGenerator, DirectionalLightHelper, WebGLRenderer, Scene, LoopOnce, Mesh, Vector3, Raycaster,

} from '../libs/three137/three.module.js';
import {EffectComposer} from '../libs/three137/pp/EffectComposer.js' ;
import {RenderPass} from '../libs/three137/pp/RenderPass.js' ;
import {ShaderPass} from '../libs/three137/pp/ShaderPass.js' ;
import {GammaCorrectionShader} from '../libs/three137/pp/GammaCorrectionShader.js' ;
import {Tween} from '../libs/Toon3D.js'

import { DRACOLoader } from '../libs/three137/DRACOLoader.js';
import { NPCHandler } from './NPCHandler.js';
import { Pathfinding } from '../libs/pathfinding/Pathfinding.js';
import { User } from './User.js';
import { Controller } from './Controller.js';
import {SFX} from './SFX.js' ;
import {UI} from './UI.js'
import { BulletHandler } from './BulletHandler.js';


let errHandler = window.console.error;
window.log = console.log;
var oldConsole = {};



// for (let key of  Reflect.ownKeys(console)){ 
// 	if( typeof console[key] === 'function'){ 
// 		oldConsole[key] = console[key] ;
// 		console[key] = function (...agrs) {
// 		// oldConsole[key](...agrs);
// 			window.log(...agrs,key ,'1')
// 		}
// 	}
// }


function myhandler(e) {
	console.log(e);
}


window.addEventListener('error', myhandler)
window.addEventListener('unhandledrejection', myhandler)
window.addEventListener('', myhandler)
const { PI } = Math;

class Game {
	constructor() {
		const container = document.createElement('div');
		document.body.appendChild(container);

		this.clock = new Clock();

		this.loadingBar = new LoadingBar();
		this.loadingBar.visible = false;


		this.camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 500);
		this.camera.position.set(-10.6, 1.6, -1.46);
		this.camera.lookAt(-5.97, .021, -1.49);

		let col = 0x201510;
		this.scene = new Scene();
		this.scene.background = new Color(col);
		this.scene.fog = new Fog(col, 100, 200); // 大概是都是近距离，没发现用了这个

		const ambient = new HemisphereLight(0xffffff, 0xbbbbff, 1);
		this.scene.add(ambient);

		//Add light with shadow

		this.dirLight = new DirectionalLight(0xffffff, 1);
		this.dirLight.position.set(4, 20, 20)
		this.dirLight.target.position.set(-2, 0, 0);
		this.dirLight.castShadow = true;

		this.dirLight.shadow.mapSize.set(1024, 512);
		let shadowCamera = this.dirLight.shadow.camera;
		const d = 30;

		shadowCamera.near = .5;
		shadowCamera.far = 60;
		shadowCamera.left = -d;
		shadowCamera.right = d;
		shadowCamera.top = d;
		shadowCamera.bottom = -d * .25; // 这个缩减 一般来说top和bottom 应该相当，这个裁剪的很厉害啊

		this.scene.add(this.dirLight)

		this.renderer = new WebGLRenderer({ antialias: true, alpha: true });
		this.renderer.shadowMap.enabled = true;
		this.renderer.setPixelRatio(window.devicePixelRatio);
		this.renderer.setSize(window.innerWidth, window.innerHeight);
		this.renderer.outputEncoding = sRGBEncoding;
		container.appendChild(this.renderer.domElement);
		this.setEnvironment();


		this.initPostProcessing()
		this.load();

		this.seeThrough = []
		this.active =false ;
		window.addEventListener('resize', this.resize.bind(this));
		this.rayCaster = new Raycaster()
		this.tempVec3 = new Vector3()
	}
	initPostProcessing(){ 
		this.composer  = new EffectComposer(this.renderer) ;
		const renderPass = new RenderPass(this.scene, this.camera) ;
		const gammaCorrectionPass = new ShaderPass(GammaCorrectionShader) ;
		const tintShader = { 
			uniforms: { 
				'tDiffuse': { value:null},
				'strength':{value:0.}
			},
			vertexShader: /*glsl*/`
			varying vec2 vUv ;
			void main(){ 
				gl_Position  = projectionMatrix * modelViewMatrix * vec4(position,1.) ;
				vUv = uv ;
			}
			`,
			fragmentShader:/*glsl*/`
			uniform float strength ;
			uniform sampler2D tDiffuse ;
			varying vec2 vUv ;
			void main() { 
				vec4 texel  = texture2D(tDiffuse, vUv) ;
				vec3 tintColor = vec3(1.,.3,.3);
				float luminance = (texel.r,texel.g + texel.b)/3. ;
				vec3 tint = tintColor * luminance * 1.8 ;
				vec3 color  = mix(texel.rgb, tint, clamp(strength, .0,1.)) ;
				gl_FragColor = vec4(color, texel.a) ;
			}
			`
		}

		this.tintPass = new ShaderPass(tintShader) ;
		this.composer.addPass(renderPass) ;
		this.composer.addPass(gammaCorrectionPass) ;
		this.composer.addPass(this.tintPass) ;
	}
	// 给屏幕上色  名词 色调 （一层）淡色
	tintScreen(action){ 
		this.tintPass.uniforms.strength.value =1 ; // 这个是shaderPass类的特征  pass可没有直接修改uniform这个本事
		const duration = action === 'shot'? 3 : 1 ;
		this.tween = new Tween(this.tintPass.uniforms.strength, 'value', 0, duration , this.removeTween)

	}

	removeTween =()=>{ 
		this.tween = null
	}

	startGame(){ 
		this.npcHandler.reset() ;
		this.user.reset();
		this.ui.ammo =1 ;
		this.ui.health =1 ;
		this.active = true ;

		this.controller.cameraBase.getWorldDirection(this.camera.position)
		this.controller.cameraBase.getWorldQuaternion(this.camera.quaternion);
		this.sfx.play('atmos')
	}
 
	gameover(){ 
		this.active = false ;
		this.ui.showGameover() ;
		this.sfx.play('atmos')
	}

	initPathfinding(navmesh) {
		this.pathfinder = new Pathfinding();
		this.waypoints = [
			new Vector3(15.76723580724466, 0.07859321352074744, -22.401987416267502),
			new Vector3(-14.398932452137636, 0.06917103459497298, -24.459471918747575),
			new Vector3(-26.89269404316095, 0.29844300481117614, 3.04072748206881),
			new Vector3(-26.330630868287024, 0.3050659033919061, 8.070409528701521),
			new Vector3(1.7960692011776445, 0.3491182029247284, 8.473453309844214),
			new Vector3(12.44035462777532, 0.015784740447998047, -10.512843193913833),

		]
		//                                                                             flowvalue  
		this.pathfinder.setZoneData('factory', Pathfinding.createZone(navmesh.geometry, .02))
		if (this.npcHandler.gltf) { this.npcHandler.initNPCs() }
	}
	seeUser(pos =new Vector3(), seeThrough =false){ 
		this.tempVec3.copy(this.user.position).sub(pos).normalize() ;
		this.rayCaster.set(pos, this.tempVec3) ;
		const intersects = this.rayCaster.intersectObjects(this.factory.children, true) // 检测子集
		this.user.visible = true 
		// 把遮挡角色的物体放进这个数组
		if(this?.seeThrough?.length){ 
			this.seeThrough.forEach((child)=> { 
				child.material.transparent = false ;
			})
		}

		if(intersects.length){ 
			const dist   = this.tempVec3.copy(this.user.position).distanceTo(pos) ; // 第一个物体如果比人物还远 那就是没有遮挡
			this.user.visible =  intersects[0].distance >   dist ;

			if(seeThrough && !this.user.visible){ 
				intersects.some((child)=> {         // 可以用some 是因为这个射线经过的物体
					if(child.distance < dist ){ 
						this.seeThrough.push(child.object) // object属性才是
						child.object.material.transparent =true ;
						child.object.material.opacity = .3 ;
					}else { return true}
					
				})
			}else { this.seeThrough = []}
		}
		return this.user.visible ;
	}

	resize() {
		this.camera.aspect = window.innerWidth / window.innerHeight;
		this.camera.updateProjectionMatrix();
		this.renderer.setSize(window.innerWidth, window.innerHeight);
	}

	setEnvironment() {
		const pmremGenerator = new PMREMGenerator(this.renderer);
		pmremGenerator.compileEquirectangularShader();

		const self = this;
		const loader = new RGBELoader()
		loader.load(urls.factory_hdr, (texture) => {
			const envMap = pmremGenerator.fromEquirectangular(texture).texture;
			pmremGenerator.dispose();

			self.scene.environment = envMap;


			// this.loadingBar.visible = !this.loadingBar.loaded;//

		}, (xhr) => {
			this.loadingBar.update('envmap', xhr.loaded, xhr.total);
		}, (err) => {
			console.error(err.message);
		});
	}

	load() {
		this.loadEnvironment();
		this.npcHandler = new NPCHandler(this)
		this.user = new User(this, new Vector3(
			-5.749308206886352
			, 
			0.015784740447998047
			,
			-4.349045253315786), PI/2);
		window.user = this.user ;	

		this.ui = new UI(this)		
	}
	loadEnvironment() {
		const loader = new GLTFLoader();

		this.loadingBar.visible = true;

		// Load a glTF resource
		loader.load(
			// resource URL
			urls.factory2_glb,
			// called when the resource is loaded
			gltf => {
				console.log(gltf.scene)
				this.factory = gltf.scene;
				this.fans = [];
				// 收集可共用一个材质的mesh
				const mergeObjects = { els2: [], els5: [], terrain: [] };

				gltf.scene.traverse((child) => {
					// if(child.type === 'Mesh'){ 
					if (child instanceof Mesh) {
						if (child.name === 'NavMesh') {

							this.navMesh = child;
							this.navMesh.geometry.rotateX(PI / 2); // blender 默认是z轴朝上的左手系
							this.navMesh.quaternion.identity();// 等于是重置旋转角， 但是，我还是不太理解 旋转一下x就行了？
							this.navMesh.position.set(0, 0, 0);
							// child.material.transparent = true;
							// child.material.opacity = .5;
							child.material.visible = false ; //和直接object的visible的区别 大概是能被 raycaster吧
							// 内部是递归实现， 这里如果直接拿走child，有可能导致for循环拿不到 出错

						}

						else if (child.name.includes('fan')) {
							this.fans.push(child)
						} else if (child.material.name.includes('elements2')) {
							mergeObjects.els2.push(child);
							child.castShadow = true;
						} else if (child.material.name.includes('elements5')) {
							mergeObjects.els5.push(child);
							child.castShadow = true;
						} else if (child.material.name.includes('terrain')) {
							mergeObjects.terrain.push(child);
							child.castShadow = true;
						} else if (child.material.name.includes('sand')) {
							child.receiveShadow = true
						} else if (child.material.name.includes('elements1')) {
							child.receiveShadow = true
							child.castShadow = true

						} else if (child.parent.name.includes('main')) {
							console.log('main');
							child.castShadow = true

						}
					}
				})



				this.scene.add(this.navMesh)
				this.initPathfinding(this.navMesh)

				for (let key in mergeObjects) {
					let mat;
					mergeObjects[key].forEach((obj) => {
						if (mat) {
							obj.material = mat
						} else {
							mat = obj.material;
						}
					})
				}

				// 初始化控制器
				this.controller = new Controller(this)

				this.scene.add(gltf.scene)
				// console.log(gltf);

				this.environmentReady = true;
				this.loadingBar.visible = !this.loadingBar.loaded;

			},
			// called while loading is progressing
			xhr => {

				this.loadingBar.update('environment', xhr.loaded, xhr.total);

			},
			// called when loading has errors
			err => {

				console.error(err);

			}
		);
	}

	initSounds(){ 
		 this.listener = new AudioListener() ;
		 this.camera.add(this.listener) ;// 相机的位置也是听觉接收的位置
		 this.sfx = new SFX(this.camera, '', this.listener) ;
		 this.sfx.load('atmos', true , .1 ) ;
		 this.user.initSounds() ;
		 this.npcHandler.npcs.forEach((npc)=> npc.initSounds())

	}

	startRendering(){ 
		if(  this.user.ready && !this.bulletHandler  && this.npcHandler.ready){
			  this.bulletHandler = new BulletHandler(this) ;
			  this.controller = new Controller(this) ;
			  this.loadingBar.visible = false;
			  this.renderer.setAnimationLoop(this.render.bind(this));

			  this.ui.visible = true ;
			  this.initSounds()
		}
	}
	render() {

		const dt = this.clock.getDelta();
		if (this.environmentReady) {

			if (this.fans?.length) {
				this.fans.forEach((fan) => fan.rotateY(dt))
			}
			if(this?.user?.ready){ 
				this.user.update(dt) 
				if(this.controller){ this.controller.update(dt)}
			}
			if (this.npcHandler) { this.npcHandler.update(dt) }
			if(this.bulletHandler){ this.bulletHandler.update(dt)}
			if(this.tween) this.tween.update(dt) ;
		
		   this.composer?this.composer.render()	: this.renderer.render(this.scene, this.camera);
		}
	}

}

export { Game };


// window.console.error = function name(...agrs) {
// 	errHandler(...agrs) ;
// 	console.log('err')
// }
// Promise.reject(2);